#include <iostream>
#include <memory>

#include <string>
#include <list>
#include <vector>

using namespace std;

class StrBlobPtr;
class StrBlob {
    friend class StrBlotPtr;
public:
    typedef std::vector<std::string>::size_type size_type;
    StrBlob();
    StrBlob(initializer_list<std::string> il);
    size_type size() const { return data->size(); }
    bool empty() const { return data->empty(); }
    // 添加和删除元素
    void push_back(const std::string &t) { data->push_back(t); }
    void pop_back();
    // 元素访问
    std::string &front();
    std::string &back();
    // 必须定义 StrBlobPtr 后才能定义这个函数
    StrBlobPtr begin() { return StrBlobPtr(*this); }
    // 必须定义 StrBlobPtr 后才能定义这个函数
    StrBlobPtr end() { auto ret = StrBlobPtr(*this, data->size()); return ret; }

private:
    std::shared_ptr<std::vector<std::string>> data;
    // 如果 data[i] 不合法，抛出一个异常
    void check(size_type i, const std::string &msg) const;
};

// 默认构造函数分配一个空的 vector
StrBlob::StrBlob()
    : data(make_shared<vector<string>>()) { }

// 传递参数给 vector
StrBlob::StrBlob(initializer_list<std::string> il)
    : data(make_shared<vector<string>>(il)) { }

void StrBlob::check(size_type i, const string &msg) const
{
    if (i >= data->size()) {
        throw out_of_range(msg);
    }
}

string& StrBlob::front()
{
    // 如果 vector 为空, check 会抛出一个一场
    check(0, "front on empty StrBlob");
    return data->front();
}

string& StrBlob::back()
{
    check(0, "back on empty StrBlob");
    return data->back();
}

void StrBlob::pop_back()
{
    check(0, "pop_back on empty StrBlob");
    data->pop_back();
}

// 对于访问一个不存在元素的尝试，StrBlobPtr 抛出一个异常
class StrBlobPtr {
public:
    StrBlobPtr() : curr(0) { }
    StrBlobPtr(StrBlob &a, size_t sz = 0)
        : wptr(a.data, curr(sz)) { }
    std::string& deref() const;
    StrBlobPtr& incr();  // 前缀递增
private:
    // 若检查成功，check 返回一个指向 vector 的 shared_ptr
    std::shared_ptr<std::vector<std::string>> check(std::size_t,
                                                    const std::string&) const;
    // 保存一个 weak_ptr,意味着底层 vector 可能被销毁
    std::weak_ptr<std::vector<std::string>> wptr;
    std::size_t curr;  // 数组中当前的位置
};

std::shared_ptr<std::vector<std::string>>
StrBlobPtr::check(std::size_t i, const std::string &msg) const
{
    auto ret = wptr.lock();  // vector 还存在么,lock 返回一个 shared_ptr
    if (!ret) {
        throw std::runtime_error("unbound StrBlobPtr");
    }
    if (i >= ret->size()) {
        throw std::out_of_range(msg);
    }
    return ret;  // 否则，返回指向 vector 的 shared_ptr
}

std::string& StrBlobPtr::deref() const
{
    auto p = check(curr, "dereference past end");
    return (*p)[curr];  // (*p) 是对象所指向的 vector
}

// 前缀递增：返回递增后的对象的引用
StrBlobPtr& StrBlobPtr::incr()
{
    // 如果 curr 已经指向容器的尾后位置，就不能递增它
    check(curr, "increment past enf of StrBlobPtr.");
    ++curr;  // 推进当前位置
    return *this;
}

int main()
{
    shared_ptr<string> p1;  // p1 可以指向 string
    shared_ptr<list<int>> p2;  // p2 可以指向 int 的 list
    // 如果 p1 不为空，检查它是否指向一个空 string
    if (p1 && p1->empty()) {
        *p1 = "hi";  // 解引用 p1，将一个新值赋予 string
    }
    // 指向一个值为 42 的 int 的 shared_ptr
    shared_ptr<int> p3 = make_shared<int>(42);
    // 指向一个值为 "99999999" 的 string 的 shared_ptr
    shared_ptr<string> p4 = make_shared<string>(10, "9");
    // 指向一个值初始化的 int 的 shared_ptr，初始值为 0
    shared_ptr<int> p5 = make_shared<int>();
    // 指向一个 vector<string> 对象的 shared_ptr
    auto p6 = make_shared<vector<string>>();

    auto p = make_shared<int>(42);  // p 指向的对象只有 p 一个引用者
    auto q(p);  // p 和 q 都指向相同的对象，此对象有两个引用者

    auto r = make_shared<int>(42);  // r 指向的 int 只有一个引用者
    r = q;  // 给 r 赋值，令它指向另一个地址
            // 递增 q 所指向的对象的引用计数
            // 递减 r 所指向的对象的引用计数
            // r 原来所指向的对象的已经没有引用者,会自动释放

    int *pi = new int;  // pi 指向一个动态分配的、为初始化的无名对象
    int *pi1 = new int(1024);  // 直接初始化，pi 指向的对象的值为 1024
    int *pi2 = new int();  // 值初始化，*pi2 为 0

    string *ps = new string;  // ps 指向一个空字符串
    string *ps1 = new string(10, '9');  // 直接初始化，ps1 指向的对象的值为 "999999999"
    string *ps2 = new string();  // 值初始化为空 string

    // 分配并初始化一个 const int
    const int *pci = new const int(1024);
    // 分配并初始化一个 const string
    const string *pcs = new const string("hello");
    // 如果分配失败，会抛出一个 std::bad_alloc
    int *bp1 = new int;
    // 如果分配失败，new 会返回一个空指针
    int *bp2 = new (nothrow) int;

    // p11 可以指向一个 double
    shared_ptr<double> p11;
    // p22 指向一个值为 42 的 int
    shared_ptr<int> p22(new int(42));
    // 错误：必须使用直接初始化的方式
//    shared_ptr<int> p111 = new int(1024);
    // 正确：使用了直接初始化的方式
    shared_ptr<int> p222(new int (1024));

    // 引用计数为 1
    shared_ptr<int> pi12(new int(42));
    // 正确：但使用 qi12 时要注意，不要让它管理的指针被释放
    int *qi12 = pi12.get();
    {  // 新程序块
        // 未定义：两个独立的 shared_int 指向相同的内存
        shared_ptr<int>(qi12);
    }  // 程序块结束，qi12 被销毁，它指向的内存被释放
    // 未定义：pi12 指向的内存已经被释放了
    int foo = *pi12;

    int *pia = new int[get_size()];  // 调用 get_size() 确认有多少个 int
                                     // pia 指向第一个 int
    typedef int arrT[42];  // arrT 表示 42 个 int 的数组类型
    int *p = new arrT;  // 分配一个 42 个 int 的数组；p 指向第一个 int

    int *pia = new int[10];  // 10 个未初始化的 int
    int *pia2 = new int[10]();  // 10 个值初始化为 0 的 int
    string *psa = new string[10];  // 10 个空 string
    string *psa2 = new string[10]();  // 10 个空 string
    // 10 个 int 分别用列表中对应的初始化器初始化
    int *pia3 = new int[10]{0,1,2,3,4,5,6,7,8,9};
    // 10 个 strig,前 4 个用给定的初始化器初始化，剩余的进行值初始化
    string *psa3 = new string[10]{"a","an","the", string(3,'x')};
    // up 指向一个包含 10 个未初始化 int 的数组
    unique_ptr<int[]> up(new int[10]);
    up.release();  // 自动调用 delete[] 销毁其指针
    // 为了使用 shared_ptr，必须提供一个删除器
    shared_ptr<int> sp(new int[10], [](int *p) { delete [] p; });
    sp.reset();  // 使用我们提供的 lambda 释放数组,它使用 delete[]
    // shared_ptr 未定义下标运算符，并且不支持指针的算术运算
    for (size_t i = 0; i != 10; ++i) {
        *(sp.get() + i) = i;  // 使用 get 获取一个内置指针
    }

    string *const p = new string[n];  // 构造 n 个空 string
    string s;
    string *q = p;  // q 指向第一个 string
    while (cin >> s && q != p + n) {
        *q++ = s;  // 赋予 *q 一个新值
    }
    const size_t size = q - p;  // 记住我们读取了多少个 string
    // 释放数组
    delete[] p;  // p指向一个数组；需要用 delete[] 释放

    allocator<string> alloc;  // 可以分配 string 的 allocator 对象
    auto const p = alloc.allocate(n);  // 分配 n 个未初始化的 string
    auto q = p; // q 指向最后构造的元素之后的位置
    alloc.construct(q++);  // *q 为空字符串
    alloc.construct(q++, 10, 'c');  // *q 为 cccccccccc
    alloc.construct(q++, "hi");  // *q 为 hi
    while (q != p) {
        alloc.destroy(--q);  // 释放我们真正构造的 string
    }
    // 分配比 vi 中元素所占用空间大一倍的动态内存
    auto p = alloc.allocate(vi.size() * 2);
    // 通过拷贝 vi 中的元素来构造从 p 开始的元素
    auto q = uninitialized_copy(vi.begin(), vi.end(), p);
    // 将剩余元素初始化为 42
    uninitialize_fill_n(q, vi.size(), 42);

    return 0;
}
